gusucode.com > DirTree 树形菜单资源管理器-源码程序 > DirTree 树形菜单资源管理器/DirTree/DirTreeView.cpp
// DirTreeView.cpp : implementation of the CDirTreeView class #include "stdafx.h" #include "DirTree.h" #include "DirTreeDoc.h" #include "DirTreeView.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CDirTreeView IMPLEMENT_DYNCREATE(CDirTreeView, CTreeView) BEGIN_MESSAGE_MAP(CDirTreeView, CTreeView) //{{AFX_MSG_MAP(CDirTreeView) ON_WM_DESTROY() ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemexpanded) ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelchanged) //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CTreeView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CTreeView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CTreeView::OnFilePrintPreview) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CDirTreeView construction/destruction CDirTreeView::CDirTreeView() { m_nLevel = 0; } CDirTreeView::~CDirTreeView() { } BOOL CDirTreeView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs cs.style |= TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS; return CTreeView::PreCreateWindow(cs); } ///////////////////////////////////////////////////////////////////////////// // CDirTreeView drawing void CDirTreeView::OnDraw(CDC* pDC) { CDirTreeDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here } void CDirTreeView::OnInitialUpdate() { CTreeCtrl& tc = GetTreeCtrl (); CTreeView::OnInitialUpdate(); LoadSystemImageList (SHGFI_SMALLICON); tc.SetImageList (&m_Images, TVSIL_NORMAL); InitializeTree (); SetDirectoryName (GetCurrentDirectory (), true); } ///////////////////////////////////////////////////////////////////////////// // CDirTreeView printing BOOL CDirTreeView::OnPreparePrinting(CPrintInfo* pInfo) { // default preparation return DoPreparePrinting(pInfo); } void CDirTreeView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add extra initialization before printing } void CDirTreeView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add cleanup after printing } ///////////////////////////////////////////////////////////////////////////// // CDirTreeView diagnostics #ifdef _DEBUG void CDirTreeView::AssertValid() const { CTreeView::AssertValid(); } void CDirTreeView::Dump(CDumpContext& dc) const { CTreeView::Dump(dc); } CDirTreeDoc* CDirTreeView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDirTreeDoc))); return (CDirTreeDoc*)m_pDocument; } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CDirTreeView message handlers void CDirTreeView::SetDirectoryName(CString & dir, bool bDisplay) { if (!strcmp ((LPCSTR) dir, ".") || dir.IsEmpty()) { m_StartPath = GetCurrentDirectory (); } else { m_StartPath = dir; } if (bDisplay) { if (m_StartPath.GetLength()) SelectStartDir ((char *) (LPCSTR)m_StartPath); } } void CDirTreeView::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; CTreeCtrl& tc = GetTreeCtrl (); // get the node that was expanded or contracted HTREEITEM node = pNMTreeView->itemNew.hItem; // Get the Tree Item for the node. Zero out the new structure. Tree control // functions don't like stray data. TV_ITEM item; memcpy ((char *) &item, (char *) &pNMTreeView->itemNew, sizeof (TV_ITEM)); // if the node was contracted then set the item data so that // later we know not to rebuild the node. if (pNMTreeView->action == TVE_COLLAPSE) { item.mask = TVIF_PARAM; item.lParam = CDirTreeView::tmExpanded; tc.SetItem(&item); return; } // If the node was already built once then we don't need to do // it again. If we do, we'll have duplicate entries if (item.lParam == CDirTreeView::tmExpanded) return; // remove the first child because we're going to add it again // remember that we have to have already added one child per // node so that the plus sign gets added to the node HTREEITEM child = tc.GetChildItem(node); if (child) tc.DeleteItem(child); // add this node and all subnodes AddNode(0, node, 0, CDirTreeView::tmDetail); *pResult = 0; } void CDirTreeView::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; CTreeCtrl& tc = GetTreeCtrl (); CWaitCursor waitcursor; // get the selected node HTREEITEM sel = tc.GetSelectedItem(); CString s; // build the path name based on the selection BuildPath(s, sel); // m_List->ListDirectory (s); *pResult = 0; } void CDirTreeView::InitializeTree () { LoadTree (); if (m_StartPath.GetLength()) SelectStartDir ((char *) (LPCSTR)m_StartPath); } void CDirTreeView::LoadTree() { char drives [_MAX_PATH]; char *aDrive, *s; // = drives; CTreeCtrl& tc = GetTreeCtrl (); HTREEITEM RootNode = tc.GetRootItem (); // Get the drive string. This is a double NUL terminated list, with // each item terminated with a NUL. // if (::GetLogicalDriveStrings(_MAX_PATH, drives)) { char CurDrive[64]; // We need the copy because we are going to // remove any trailing backslash, which would // screw up our double NUL terminated list. aDrive = drives; // Parse the drives do { UINT type; // Get a drive strcpy (CurDrive, aDrive); type = ::GetDriveType(CurDrive); if ((s = strchr (CurDrive, '\\')) != NULL) *s = '\0'; // Make sure the drive letter is caps and add the node for the drive. strupr (CurDrive); AddNode(CurDrive, RootNode, type, CDirTreeView::tmShort); aDrive += strlen (aDrive) + 1; } while(strlen (aDrive)); } } void CDirTreeView::RefreshSelection() { CString strSelected; CTreeCtrl& tc = GetTreeCtrl (); UINT state; // Pop up a wait cursor CWaitCursor waitcursor; // get the selected node HTREEITEM sel = tc.GetSelectedItem(); HTREEITEM parent = tc.GetParentItem(sel); state = tc.GetItemState (parent, TVIF_STATE); strSelected = tc.GetItemText (sel); tc.DeleteItem (sel); AddNode ((LPCSTR) strSelected, parent, 0, CDirTreeView::tmShort); BuildPath (strSelected, parent); SelectStartDir ((char *) (LPCSTR)strSelected); m_StartPath = strSelected; sel = tc.GetSelectedItem(); if (state & TVIS_EXPANDED) tc.Expand (sel, TVE_EXPAND); // Always expand } void CDirTreeView::DeleteNode(CString & NodeName) { } // AddNode(). This function adds the nodes to the tree. The first // time we display a node we don't want to add each and every // sub-directory to the tree. We only want to add nodes when // necessary or it will take forever for the dialog box to come // up initially. But, we have to add at least one node so that // the + sign shows up indicating that sub-directories exist. void CDirTreeView::AddNode(const char * path, HTREEITEM node, int type, int mode) { WIN32_FIND_DATA fd; HANDLE hFind; char buff [_MAX_PATH]; // temporary storage HTREEITEM newNode = node; // May be used to build pathname without int image1, image2; // adding any new nodes. CString dirPath; // If a node name was passed to us then we will add the node. // Otherwise, we need to get a directory name first and then recurse. // if (path) { // if the mode indicates that we are adding a drive node // then we need to: // add the drive label in parens. // reset the mode to tmShort (actually, on fast machines this // doesn't really speed things up). if (type >= DRIVE_REMOVABLE) { char VolName[24]; char RootName[10]; DWORD dwCompLen, dwFlags; memset (&fd, '\0', sizeof (WIN32_FIND_DATA)); fd.dwFileAttributes = FILE_ATTRIBUTE_SYSTEM; sprintf (RootName, "%s\\", path); memset (VolName, '\0', sizeof (VolName)); strcpy (fd.cFileName, RootName); GetVolumeInformation (RootName, VolName, 24, NULL, &dwCompLen, &dwFlags, NULL, 0); if (strlen (VolName)) { wsprintf(buff, "%s (%s)", path, VolName); } else wsprintf (buff, "%s", path); image1 = image2 = GetIconIndex (fd); newNode = InsertChild (node, buff, image1, image2, TVI_LAST); mode = CDirTreeView::tmShort; } // Otherwise just use the node name passed else { strcpy(buff, path); // Add the node as a child to the current node using the folder images. // Use the sort flag to sort the tree as we go.so the list is sorted // as we go. memset (&fd, '\0', sizeof (WIN32_FIND_DATA)); strcpy (fd.cFileName, buff); fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; image1 = GetIconIndex (fd); fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY | SHGFI_OPENICON; image2 = GetIconIndex (fd); newNode = InsertChild (node, buff, image1, image2, TVI_SORT); } } // Build a path name based on the node. BuildPath(dirPath, newNode); // Add wildcards dirPath += "*.*"; // // Look for the first match. Return if none. if ((hFind = FindFirstFile(dirPath, &fd)) == INVALID_HANDLE_VALUE) { return; } // add one to the level we are on m_nLevel++; do { if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (!(fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))) { if (!strcmp (fd.cFileName, ".")) { ; // Current directory. Do nothing. } else if (!strcmp (fd.cFileName, "..")) { ; // Parent directory. Do nothing. } else { // If the are building the intial tree structure (tmShort) or we are // expanding a level (tmDetail), add the node by recursion. if (((mode == CDirTreeView::tmShort) && (m_nLevel < 2)) || (mode == CDirTreeView::tmDetail)) AddNode(fd.cFileName, newNode, 0, mode); // type, 0); // mode); // If we're building the initial structure, we want to add only one // subnode to make the plus symbold appear. if (mode == CDirTreeView::tmShort) break; // In the detail mode we need to fill this branch completely // but only one sub-node per node under this branch. Again, // we have to do this so the + sign shows up next to the node. if (mode == CDirTreeView::tmDetail && m_nLevel > 1) { break; } } } } while (::FindNextFile (hFind, &fd)); // Look for the next match FindClose (hFind); // decrement the level counter m_nLevel--; } // // InsertChild() inserts a new node in the tree as a child of the node // passed as the parent.Images default to 0 and SortType default to TVI_SORT // HTREEITEM CDirTreeView::InsertChild(HTREEITEM parent, char * label, int image, int selimage, HTREEITEM SortType) { CTreeCtrl& tc = GetTreeCtrl (); TV_INSERTSTRUCT TreeItem; // // Zero out the new structure. CTreeCtrl doesn't like stray data. // memset ((char *) &TreeItem, '\0', sizeof (TV_INSERTSTRUCT)); TreeItem.hParent = parent == NULL ? TVI_ROOT : parent; TreeItem.hInsertAfter = SortType; TreeItem.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE; TreeItem.item.pszText = label; TreeItem.item.cchTextMax = strlen (label) + 1; TreeItem.item.lParam = 0; TreeItem.item.iImage = image; TreeItem.item.iSelectedImage = image; TreeItem.item.iSelectedImage = selimage; return (tc.InsertItem(&TreeItem)); } void CDirTreeView::SelectStartDir(char * StartDir) { char *startDir, Drive[32]; char *s; CTreeCtrl& tc = GetTreeCtrl (); HTREEITEM RootNode, node, SelectedNode; if ((StartDir == NULL) || !strlen (StartDir)) return; // // Make a copy of the directory string. // startDir = new char [strlen (StartDir) + 1]; strcpy (startDir, StartDir); // // Make sure the path contains no forward slashes. // while ((s = strchr (startDir, '/')) != NULL) *s = '\\'; s = strtok (startDir, "\\"); if (s == NULL) { s = startDir; } // // Make it all upper case. Less confusion that way. Our first node // should be the device. Append the trailing backslash, which is the // way the node is returned from BuildPath // // strupr (s); sprintf (Drive, "%s\\", s); // Get the root node RootNode = tc.GetRootItem (); // Get the first child item of the root node node = tc.GetNextItem(RootNode, TVGN_ROOT); // // Loop through the siblings until we find our drive // for (; node; node = tc.GetNextItem(node, TVGN_NEXT)) { CString ItemText; BuildPath (ItemText, node); // // If the root paths match, we've found it // if (!strcmpi (Drive, (LPCSTR) ItemText)) break; } // // Did we find the start for our directory? // if (node == NULL) { delete [] startDir; return; } // // Select this item just in case the next strtok is NULL // SelectedNode = node; s = strtok (NULL, "\\"); // Get the next part of the path if (s == NULL) // If none, we are finished. { tc.Select (SelectedNode, TVGN_CARET); delete [] startDir; return; } // // There's more to look for, so expand this item. // tc.Expand (node, TVE_EXPAND); // Always expand while (s != NULL) { // for (node = tc.GetNextItem(node, TVGN_CHILD); for (node = tc.GetNextItem(SelectedNode, TVGN_CHILD); node; node = tc.GetNextItem(node, TVGN_NEXT)) { CString ItemText = tc.GetItemText (node); if (!ItemText.CompareNoCase (s)) break; } // // If the node was not found, we can't continue. Leave the current // selection as it is. // if (node == NULL) break; // // Select the current item. // SelectedNode = node; s = strtok (NULL, "\\"); // If the next token is not null, expand the current item. if (s != NULL) tc.Expand (node, TVE_EXPAND); // Always expand } tc.Select (SelectedNode, TVGN_CARET); delete [] startDir; } // BuildPath() builds the path name by recursing backward // through the tree. // void CDirTreeView::BuildPath(CString & path, HTREEITEM node) { CTreeCtrl& tc = GetTreeCtrl (); // get the parent of the current node HTREEITEM parent = tc.GetParentItem(node); // Get the text of the node. We'll use it to build the path CString buff = tc.GetItemText (node); // add the backslash to the node buff += "\\"; // Add the current directory name to the path. // buff += path; path = buff; // If this node has a parent then we recurse until no parent is found. // If there is no parent, then we are finished building the path. // if (parent) { BuildPath(path, parent); } else { // If the parent is found then we need to strip the volume name // from the string. It is enclosed in parentheses, so look for the // first open paren and pick up the left side, then find the first // close paren and pick up the right part. // CString dPath = path; int Length = path.GetLength (); int pos = dPath.Find("("); if (pos > 0) path = dPath.Left (pos - 1); pos = dPath.Find (")"); if (pos > 0) path += dPath.Right (Length - pos - 1); } } void CDirTreeView::OnDestroy() { m_Images.Detach(); CTreeView::OnDestroy (); } int CDirTreeView::GetIconIndex (WIN32_FIND_DATA fd) { SHFILEINFO sfi; memset(&sfi, 0, sizeof(sfi)); CString strFilePath = fd.cFileName; if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { SHGetFileInfo ( strFilePath, FILE_ATTRIBUTE_DIRECTORY, &sfi, sizeof(sfi), SHGFI_SMALLICON | SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES | (fd.dwFileAttributes & SHGFI_OPENICON) ); return (sfi.iIcon); } else { SHGetFileInfo ( strFilePath, FILE_ATTRIBUTE_NORMAL, // fd.dwFileAttributes, &sfi, sizeof(sfi), // SHGFI_SMALLICON | SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES | (fd.dwFileAttributes & SHGFI_OPENICON) SHGFI_SMALLICON | SHGFI_SYSICONINDEX | (fd.dwFileAttributes & SHGFI_OPENICON) ); return sfi.iIcon; } return (-1); } BOOL CDirTreeView::LoadSystemImageList(UINT uiImageList) { SHFILEINFO sfiInfo; memset(&sfiInfo, 0, sizeof(SHFILEINFO)); HIMAGELIST hImages = (HIMAGELIST) ( SHGetFileInfo ( "C:\\", 0, &sfiInfo, sizeof(sfiInfo), SHGFI_SYSICONINDEX | uiImageList) ); if (hImages == NULL) return (FALSE); switch (uiImageList) { case SHGFI_SMALLICON: if (m_Images.m_hImageList != NULL) m_Images.Detach (); m_Images.Attach (hImages); break; default: return (FALSE); } return (TRUE); } CString & CDirTreeView::GetCurrentDirectory() { static CString strCurrentPath; strCurrentPath.Empty (); int nBytes = ::GetCurrentDirectory (0, NULL); char *szDir = new char [nBytes + 1]; ::GetCurrentDirectory (nBytes + 1, szDir); strCurrentPath = szDir; delete [] szDir; return (strCurrentPath); }